package com.jinzhi.jzapi.utils;

import com.alibaba.fastjson.JSONObject;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.InvalidClaimException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.jinzhi.jzapi.domain.UserDO;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.util.*;

/**
 * <pre>
 * jwt工具类
 * </pre>
 *
 * <small> 2018年4月28日 | Aron</small>
 */
@Slf4j
public class JWTUtil {

    public static HttpServletRequest getRequest() {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        return requestAttributes == null ? null : ((ServletRequestAttributes)requestAttributes).getRequest();
    }

    public static String STR_DELIMITER = "\\.";
    public static int LEVEL = 3;

    public static UserDO getUser() {
        HttpServletRequest request = JWTUtil.getRequest();
        if (request == null) {
            return null;
        } else {
            Object bladeUser = request.getAttribute("_BLADE_USER_REQUEST_ATTR_");
            if (bladeUser == null) {
                bladeUser = getUser(request);
                if (bladeUser != null) {
                    request.setAttribute("_BLADE_USER_REQUEST_ATTR_", bladeUser);
                }
            }
            return (UserDO) bladeUser;
        }
    }
    /*
     *解析token
     *  build = JWT.require(Algorithm.HMAC256(secret)).build();
     */
    public static Claims parseJWT(String token) {
        // 得到的是荷载
        Claims claims = (Claims) Jwts.parser()
                // 解析时的秘钥一定要和签发时秘钥相同，但是发现这了的秘钥为4-7个x都是可以成功解析的
                .setSigningKey(JwtConfigTime.JwtVerify)
                // .parse(token)
                .parseClaimsJws(token)
                .getBody();

        return claims;
    }
    public static Claims getClaims(HttpServletRequest request) {
        String token= request.getHeader("Blade-Auth");
        Claims claims = null;
        claims = parseJWT(token);

        return claims;
    }

    public static String toStr(Object str) {
        return toStr(str, "");
    }

    public static String toStr(Object str, String defaultValue) {
        return null != str && !str.equals("null") ? String.valueOf(str) : defaultValue;
    }

    /**
     * 解析token
     * @param request
     * @return
     */
    public static UserDO getUser(HttpServletRequest request) {
        String token= request.getHeader("Blade-Auth");
        if(StringUtils.isBlank(token)){
          //  int status = ExceptionEnum.JWT_TOKEN_NULL.getStatus();
          //  String message = ExceptionEnum.JWT_TOKEN_NULL.getMessage();
           // throw new JinZhiException(status,message);
            throw new JinZhiException(ExceptionEnum.JWT_TOKEN_NULL.getStatus(),ExceptionEnum.JWT_TOKEN_NULL.getMessage());
        }
        UserDO userDO=  jzVerify(token,JwtConfigTime.JwtVerify);
         return userDO;
    }

    public static UserDO getJwt(HttpServletRequest request){
        Claims claims = getClaims(request);
           String username = toStr(claims.get("username"));
           String userId = toStr(claims.get("userId"));
             String mobile = toStr(claims.get("mobile"));
           String integral = toStr(claims.get("integral"));

        String test = toStr(claims.get("test"));
//            String orgName = toStr(claims.get("orgName"));
//
            UserDO userDO = new UserDO();
            userDO.setUsername( username);
            userDO.setMobile( mobile);
     //   userDO.setIntegral(integral);
        userDO.setName(test);
        userDO.setMobile( mobile);
            return userDO;

    }
    public static String createJWT(Map<String, Object> claims, Long time) {
        claims.put("test","test001");
        //指定签名的时候使用的签名算法，也就是header那部分，jjwt已经将这部分内容封装好了。
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        Date now = new Date(System.currentTimeMillis());
        //生成JWT的时间
        long nowMillis = System.currentTimeMillis();
        //下面就是在为payload添加各种标准声明和私有声明了
        //这里其实就是new一个JwtBuilder，设置jwt的body
        JwtBuilder builder = Jwts.builder()
                //如果有私有声明，一定要先设置这个自己创建的私有的声明，这个是给builder的claim赋值，一旦写在标准的声明赋值之后，就是覆盖了那些标准的声明的
                .setClaims(claims)
                .setIssuedAt(now)
                //设置签名使用的签名算法和签名使用的秘钥
                .signWith(signatureAlgorithm, JwtConfigTime.JwtVerify);
        if (time >= 0) {
            long expMillis = nowMillis + time;
            Date exp = new Date(expMillis);
            //设置过期时间
            builder.setExpiration(exp);
        }
        return builder.compact();
        //然后返回token
    }
    /**
     * token是否过期
     *
     * @return true：过期
     */
    public static boolean isTokenExpired(String token) {
        Date now = Calendar.getInstance().getTime();
        DecodedJWT jwt = JWT.decode(token);
        return jwt.getExpiresAt().before(now);
    }

    public static void main(String[] args) {
      String token=  jzSign(new UserDO(),JwtConfigTime.JwtVerify,0l);
       boolean b= isTokenExpired(token);
        System.out.println("过期状态="+b);

    }
    /**
     * <pre>
     * </pre>
     *
     * <small> 2018年4月28日 | Aron</small>
     *
     * @param token  即jwt
     * @param userId 用户id
     * @param secret 用户的secret
     */
    public static void verify(String token, String userId, String secret) {
        try {
            Algorithm algorithm = Algorithm.HMAC256(secret);
            JWTVerifier verifier = JWT.require(algorithm).withClaim(JwtConfigTime.JwtVerify, userId).build();
            verifier.verify(token);
        } catch (TokenExpiredException exception) {
            log.info("token 签名校验失败,过期：{}", token);
            throw new JinZhiException(ExceptionEnum.TOKENFIAL.getStatus(),ExceptionEnum.TOKENFIAL.getMessage());
        } catch (InvalidClaimException exception2) {
            log.info("token 签名校验失败,数据异常：{}", token);
            throw new JinZhiException(ExceptionEnum.TOKENFIAL.getStatus(),ExceptionEnum.TOKENFIAL.getMessage());
        } catch (Exception exception3) {
            log.info("token 签名校验失败：{}", token);
            //   throw new IFastApiException(EnumErrorCode.apiAuthorizationInvalid.getCodeStr());
           throw new JinZhiException(ExceptionEnum.TOKENFIAL.getStatus(),ExceptionEnum.TOKENFIAL.getMessage());
        }
    }


    /**
     * <pre>
     * </pre>
     *
     * <small> 2018年4月28日 | Aron</small>
     *1212
     * @param token  即jwt
     * @param secret 用户的secret
     */
    public static void jzverify(String token) {
      //  throw new JinZhiException(ExceptionEnum.NO_TOKEN.getStatus(),ExceptionEnum.NO_TOKEN.getMessage());
        try {
            Algorithm algorithm = Algorithm.HMAC256(JwtConfigTime.JwtVerify);
            JWTVerifier verifier = JWT.require(algorithm).build();
            verifier.verify(token);


        } catch (TokenExpiredException exception) {
            log.info("token 签名校验失败,过期：{}", token);
            throw new JinZhiException(ExceptionEnum.NO_TOKEN.getStatus(),ExceptionEnum.NO_TOKEN.getMessage());
        } catch (InvalidClaimException exception2) {
            log.info("token 签名校验失败,数据异常：{}", token);
            throw new JinZhiException(ExceptionEnum.NO_TOKEN.getStatus(),ExceptionEnum.NO_TOKEN.getMessage());
        } catch (Exception exception3) {
            log.info("token 签名校验失败：{}", token);
            // throw new IFastApiException(EnumErrorCode.apiAuthorizationInvalid.getCodeStr());
            throw new JinZhiException(ExceptionEnum.NO_TOKEN.getStatus(),ExceptionEnum.NO_TOKEN.getMessage());
        }
    }

//    public static void verify(String token, String userId, String secret, boolean isRefreshToken) {
//        if (isRefreshToken) {
//            secret += "_REFRESH_TOKEN";
//        }
//        verify(token, userId, secret);
//    }

    /**
     * <pre>
     * </pre>
     *
     * <small> 2018年4月28日 | Aron</small>
     *
     * @param secret 加密密钥
     * @param expire 有效期，毫秒值
     */
    public static String jzSign(UserDO userDO, String secret, Long expire) {
        // 过期时间
        final Calendar instance = Calendar.getInstance();
        //设置过期时间
        instance.add(Calendar.SECOND, expire.intValue());
        try {
            final String token;
            Algorithm algorithm = Algorithm.HMAC256(secret);
            // 以abc为密钥进行hmac256方法加密，保存 a,b两个属性

            token = JWT.create().withHeader(new HashMap<>())
                    .withClaim("username", userDO.getName())
                    .withClaim("userId", userDO.getId())
                    .withClaim("mobile", userDO.getMobile())
                    .withClaim("name", userDO.getName())
                    .withClaim("orgId", userDO.getOrgId())
                    .withClaim("salt", userDO.getSalt())
                   // .withClaim("integral", userDO.getIntegral())
                    .withExpiresAt(instance.getTime())
                    .sign(algorithm);
            return token;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        return null;

    }

    /**
     * <pre>
     * </pre>
     *
     * <small> 2018年4月28日 | Aron</small>
     *
     * @param userId 用户标识
     * @param secret 加密密钥
     * @param expire 有效期，毫秒值
     */
    public static String sign(String userId, String secret,Long expire) {
        // 过期时间
        final Calendar instance = Calendar.getInstance();
        //设置过期时间
        instance.add(Calendar.SECOND, JwtConfigTime.time5);

        try {
            final String token;
            Algorithm algorithm = Algorithm.HMAC256(secret);
            // 以abc为密钥进行hmac256方法加密，保存 a,b两个属性

            token = JWT.create().withHeader(new HashMap<>())
                    .withClaim("a", "ccc")
                    .withClaim("b", JSONObject.toJSON(Arrays.asList("a", "b", "c")).toString())
                    .withExpiresAt(instance.getTime())
                    .sign(algorithm);
            return token;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
       return null;
    }

    /**
     *   .withClaim("username", userDO.getName())
     *                     .withClaim("userId", userDO.getId())
     *                     .withClaim("mobile", userDO.getMobile())
     *                     .withClaim("name", userDO.getName())
     *                     .withClaim("orgName", userDO.getOrgName())
     *                     .withClaim("orgId", userDO.getOrgId())
     *                     .withClaim("salt", userDO.getSalt())
     * @param token
     * @param secret
     * @return
     */
    public static UserDO jzVerify(String token, String secret) {
        UserDO userDO=new UserDO();
        JWTVerifier build = null;

        try {
            jzverify(token);
            build = JWT.require(Algorithm.HMAC256(secret)).build();
            if(isTokenExpired(token)){
                throw new JinZhiException(ExceptionEnum.JWT_TOKEN_FAIL.getStatus(), ExceptionEnum.JWT_TOKEN_FAIL.getMessage());
            }
            final DecodedJWT verify = build.verify(token);
            // 取出a
            userDO.setUsername( verify.getClaim("username").asString());
         //   userDO.setIntegral( verify.getClaim("integral").asString());
            userDO.setId( verify.getClaim("userId").asLong());
            userDO.setMobile( verify.getClaim("mobile").asString());
            userDO.setName( verify.getClaim("name").asString());
            userDO.setOrgId( verify.getClaim("orgId").asLong());
            userDO.setSalt( verify.getClaim("salt").asString());
            // 取出过期时间
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        return userDO;
    }

//    /**
//     * <pre>
//     * </pre>
//     *
//     * <small> 2018年4月28日 | Aron</small>
//     *
//     * @param userId 用户标识
//     * @param secret 加密密钥
//     * @param expire 有效期，毫秒值
//     */
//    public static String sign(String userId, String secret, long expire, boolean isRefreshToken) {
//        if (isRefreshToken) {
//            secret += "_REFRESH_TOKEN";
//        }
//        return sign(userId, secret, expire);
//    }

}
